package petrivis.model;

import java.io.Serializable;
import prefuse.data.Edge;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.util.collections.IntIterator;

/**
 * Represents a Petri Net
 * @author Jorge Munoz
 */
public class PetriNet extends Graph implements Serializable{

    /** Name Column */
    public static final String NAME_COL = "name";
    /** Place Column */
    public static final String PLACE_COL = "place";
    /** Capacity Column */
    public static final String CAP_COL = "capacity";
    /** Initial Marking Column*/
    public static final String INI_MAR_COL  = "initialmarking";
    /** Marking Column*/
    public static final String MAR_COL  = "marking";
    /** Weight Column*/
    public static final String WEIGHT_COL  = "weight";
    /** Source Column*/
    public static final String SOURCE_COL  = "source";
    /** Target Column*/
    public static final String TARGET_COL  = "target";

    /**
     * Creates a new, empty Petri Net.
     */
    public PetriNet(){
        //Create a empty Graph with the given structure that will be the PetriNet
        super(new Nodes(), new Arcs(), true, null, SOURCE_COL,TARGET_COL);
    }

    /**
     * Add an Arc between source Node and target Node (they must exist)
     * @param sourceName Name of the source Node (Must exist)
     * @param targetName Name of the target Node (Must exist)
     * @return the Arc just added
     */
    public Edge addArc(String sourceName, String targetName){
        Node source = getNode(sourceName);
        Node target = getNode(targetName);
        Edge edge = addEdge(source,target);
        return edge;
    }

    /**
     * Add an Arc between source Node and target Node (they must exist)
     * @param sourceName Name of the source Node (Must exist)
     * @param targetName Name of the target Node (Must exist)
     * @param weight weight of the arc
     * @return the Arc just added
     */
    public Edge addArc(String sourceName, String targetName, int weight){
        Edge arc = addArc(sourceName,targetName);
        arc.set(WEIGHT_COL, weight);
        return arc;
    }

    /**
     * Add a Place to the Petri Net (if it is not there) and returns it
     * @param placeName Name of the Place
     * @return The Place with the given name
     */
    public Node addPlace(String placeName){
        Node place = getNode(placeName);
        //If the place doesn't exist, create it
        if(place == null){
            place = addNode();
            place.set(NAME_COL, placeName);
            place.set(PLACE_COL,true);
            place.set(INI_MAR_COL, 0);
            place.set(MAR_COL,0);
        }
        return place;
    }

    /**
     * Add a Transition to the Petri Net (if it is not there) and returns it
     * @param transitionName Name of the Transition
     * @return The Transition with the given name
     */
    public Node addTransition(String transitionName){
        Node transition = getNode(transitionName);
        //If the transition doesn't exist, create it
        if(transition == null){
            transition = addNode();
            transition.set(NAME_COL,transitionName);
            transition.set(PLACE_COL, false);
        }
        return transition;
    }
    
    /**
     * Set the marking of the given place
     * @param placeName Name of the Place
     * @param marking Marking to set
     */
    public void setMarking(String placeName, int marking){
        Node place = getNode(placeName);
        place.set(INI_MAR_COL, marking);
        place.set(MAR_COL, marking);
    }

    //TODO setCapacity()

    /**
     * Returns if a node is a place, a transition or doesn't exist
     * @param nodeName Name of the Node
     * @return If the node doesn't exists -> null
               If it is a Place -> true
               If it is a Transition -> false
     */
    public Boolean isPlace(String nodeName){
        Boolean returnBool = null;
        Node node = getNode(nodeName);

        //If the node doesn't exists -> null
        //If it is a Place -> true
        //If it is a Transition -> false
        if(node == null) returnBool = null;
        else if((Boolean)node.get(PLACE_COL) == true)
            returnBool = true;
        else if((Boolean)node.get(PLACE_COL) == false)
            returnBool = false;

        return returnBool;
    }


    

    /**
     * Return the Node of the given name (or null if it doesn't exist)
     * @param nodeName Name of the node to get
     * @return The Node if the node exists (or null if it doesn't)
     */
    private Node getNode(String nodeName){
        Node returnNode;

        //Get an iterator over all the rows with the
        //given name (0 or 1 in theory)
        IntIterator it = getNodeTable().getIndex(NAME_COL)
                .rows(nodeName);

        //If it exists a row, get the number of the row and use it to get the Node
        if(it.hasNext()){
            int rowId = it.nextInt();
            returnNode = getNode(rowId);
        }
        //If the row doesn't exist, then return null
        else{
            returnNode = null;
        }

        return returnNode;
    }


}
